1 Exploring purr functions

from:

http://www.rebeccabarter.com/blog/2019-08-19_purrr/

https://adv-r.hadley.nz/functionals.html#purrr-style

1.1 options & settings

options(scipen = 999)

1.2 Libs

library(tidyverse)

1.3 gapminder data

# to download the data directly:

gapminder_orig <- read.csv("https://raw.githubusercontent.com/swcarpentry/r-novice-gapminder/gh-pages/_episodes_rmd/data/gapminder-FiveYearData.csv")

# define a copy of the original dataset that we will clean and play with 
gapminder <- gapminder_orig
names(gapminder)
[1] "country"   "year"      "pop"       "continent" "lifeExp"   "gdpPercap"
dim(gapminder)
[1] 1704    6

1.4 map & modify from purr

1.4.1 class

class(gapminder$country)
[1] "character"
gapminder %>% map_chr(class)
    country        year         pop   continent     lifeExp   gdpPercap 
"character"   "integer"   "numeric" "character"   "numeric"   "numeric" 

modify() returns in the same output format as input, so it is not a suitable choice in this case

1.4.2 n_distinct

gapminder %>% map_dbl(n_distinct)
  country      year       pop continent   lifeExp gdpPercap 
      142        12      1704         5      1626      1704 

1.4.3 class + n_distinct

make sure to pass .x otherwise it will not perform action on columns

Note : we have missed the column names above

Adding column names

defining .x

continents <- continent_year %>% 
                pull(continent) %>% 
                as.character

years <- continent_year %>% 
            pull(year)

.x <- continents[1]
.y <- years[1]
  
gapminder %>% 
  filter(continent == .x,
         year == .y) %>% 
  
  ggplot() +
  geom_point(aes(x = gdpPercap, y = lifeExp, col = country)) +
  ggtitle(paste(.x, .y))

Applying above test code for generic usage object

plot_list <- map2(.x = continents, .y = years,
     .f = ~{gapminder %>% 
  filter(continent == .x,
         year == .y) %>% 
  
  ggplot() +
  geom_point(aes(x = gdpPercap, y = lifeExp, col = country)) +
  ggtitle(paste(.x, .y))})
plot_list[1]
[[1]]

Below I nest the gapminder data by continent.

gapminder_nested <- gapminder %>% 
                      group_by(continent) %>% 
                      nest()

gapminder_nested
gapminder_nested$data[1]
[[1]]
NA

To pull or extract from it by index

gapminder_nested %>% 
  pluck(1)
[1] "Asia"     "Europe"   "Africa"   "Americas" "Oceania" 

To pull or extract data from it by index

gapminder_nested %>% 
  pluck("data",1)

since map returns a lits itself, so we will need to pull from the list

tibble(list_col = list(c(1, 5, 7),
                       5,
                       c(10, 10, 11))) %>% 
    mutate(list_sum = map(.x = list_col, .f = sum)) %>% 
  pull(list_sum)
[[1]]
[1] 13

[[2]]
[1] 5

[[3]]
[1] 31

it could be better to result out a ve tor instead of list

tibble(list_col = list(c(1, 5, 7),
                       5,
                       c(10, 10, 11))) %>% 
  mutate(list_sum = map_dbl(.x = list_col, .f = sum))

How to get mean from column listed tibble data

.x <- gapminder_nested %>% 
        pluck("data", 1)
mean(.x$lifeExp)
[1] 60.0649

Now applying mean function on all column listed tible data

gapminder_nested %>% 
  mutate(avg_lifeExp = map_dbl(data, ~{mean(.x$lifeExp)}))

1.4.4 fitting a linear model for each contines / row

gapminder_nested <- gapminder_nested %>% 
  mutate(lm_obj = map(data, ~lm(lifeExp ~ pop + gdpPercap + year, data = .x)))

gapminder_nested

1.4.4.1 checking linear model for first continent

gapminder_nested %>% pluck("lm_obj", 1)

Call:
lm(formula = lifeExp ~ pop + gdpPercap + year, data = .x)

Coefficients:
(Intercept)          pop   gdpPercap        year 
 -7.833e+02   4.228e-11   2.510e-04   4.251e-01 

1.4.5 Adding Predictions

gapminder_nested <- gapminder_nested %>% 
  mutate(pred = map2(.x = lm_obj, .y = data, function(.x,.y) predict(.x, .y)))

gapminder_nested

can also be written as

1.4.5.1 Calc. correlation pred reps. vs true resp.

gapminder %>% 
  group_by(continent) %>% 
  nest %>% 
  mutate(lm_obj = map(data, ~lm(lifeExp ~ pop + year + gdpPercap, data = .))) %>% 
  mutate(lm_tidy = map(lm_obj, broom::tidy))
gapminder %>% 
  group_by(continent) %>% 
  nest %>% 
  mutate(lm_obj = map(data, ~lm(lifeExp ~ pop + year + gdpPercap, data = .))) %>% 
  mutate(lm_tidy = map(lm_obj, broom::tidy)) %>% 
  ungroup() %>% 
  transmute(continent, lm_tidy) %>% 
  unnest(cols = c(lm_tidy))

1.4.6 split function

this will split the data frame on basisi of factors provided from the variable

gapminder %>% split(gapminder$continent)
$Africa

$Americas

$Asia

$Europe

$Oceania
NA
set.seed(23489)

gapminder_list <- gapminder %>% 
  split(gapminder$continent) %>% 
  map(~sample_n(., 5))

gapminder_list
$Africa

$Americas

$Asia

$Europe

$Oceania
NA

1.4.7 keep()

function to limit/ filter data frame with conditions

discar() is opposite of keep

gapminder_list %>% 
  keep(~{mean(.x$lifeExp) > 70})
$Americas

$Europe

$Oceania
NA
NA

1.4.8 Reduce()

reduce() is designed to combine (reduces) all of the elements of a list into a single object by iteratively applying a binary function (a function that takes two inputs).

reduce(c(1, 2, 3), sum)
[1] 6

1.4.9 accumulate()

also returns the intermediate values.

accumulate(c(1, 2, 3), sum)
[1] 1 3 6

Reduce can be useful in combining columns by using left_join etc. or to do repeated rbind()

1.4.10 Logical statements for lists

every(), some()

For instance to ask whether every continent has average life expectancy greater than 70, you can use every()

gapminder_list %>% every(~{mean(.x$life) > 70 })
[1] FALSE
gapminder_list %>% some(~{mean(.x$life) > 70})
[1] TRUE

1.4.11 has_element()

this is equivalent of %in%

list(1, c(2, 5, 1), "a") %>% has_element("a")
[1] TRUE
LS0tDQp0aXRsZTogInB1cnIgJiBmdW5jdGlvbnMgZXhwbG9yYXRpb24iDQpvdXRwdXQ6IA0KICBodG1sX25vdGVib29rOg0KICAgIHRoZW1lOiBzcGFjZWxhYg0KICAgIGhpZ2hsaWdodDogdGFuZ28NCiAgICBkZl9wcmludDogcGFnZWQNCiAgICB0b2M6IHRydWUNCiAgICB0b2NfZmxvYXQ6IA0KICAgICAgY29sbGFwc2VkOiBmYWxzZQ0KICAgICAgc21vb3RoX3Njcm9sbDogZmFsc2UNCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUNCiAgICB0b2NfZGVwdGg6IDYNCi0tLQ0KDQoNCjxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI+DQoNCmJvZHksIHRkIHsNCiAgIGZvbnQtZmFtaWx5OiAiT0NSLUIgMTAgQlQiOw0KfQ0KY29kZS5yew0KICBmb250LWZhbWlseTogIk9DUi1CIDEwIEJUIjsNCn0NCnByZSB7DQogIGZvbnQtZmFtaWx5OiAiT0NSLUIgMTAgQlQiOw0KfQ0KPC9zdHlsZT4NCg0KDQoNCiMgRXhwbG9yaW5nIHB1cnIgZnVuY3Rpb25zDQoNCmZyb206IA0KDQpodHRwOi8vd3d3LnJlYmVjY2FiYXJ0ZXIuY29tL2Jsb2cvMjAxOS0wOC0xOV9wdXJyci8NCg0KaHR0cHM6Ly9hZHYtci5oYWRsZXkubnovZnVuY3Rpb25hbHMuaHRtbCNwdXJyci1zdHlsZQ0KDQojIyBvcHRpb25zICYgc2V0dGluZ3MNCg0KYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9DQprbml0cjo6b3B0c19jaHVuayRzZXQoZWNobyA9IFRSVUUsIHdhcm5pbmcgPSBGQUxTRSwgbWVzc2FnZSA9IEZBTFNFKQ0KYGBgDQoNCmBgYHtyfQ0Kb3B0aW9ucyhzY2lwZW4gPSA5OTkpDQpgYGANCg0KDQojIyBMaWJzDQoNCmBgYHtyfQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpgYGANCg0KDQojIyBnYXBtaW5kZXIgZGF0YQ0KDQpgYGB7cn0NCiMgdG8gZG93bmxvYWQgdGhlIGRhdGEgZGlyZWN0bHk6DQoNCmdhcG1pbmRlcl9vcmlnIDwtIHJlYWQuY3N2KCJodHRwczovL3Jhdy5naXRodWJ1c2VyY29udGVudC5jb20vc3djYXJwZW50cnkvci1ub3ZpY2UtZ2FwbWluZGVyL2doLXBhZ2VzL19lcGlzb2Rlc19ybWQvZGF0YS9nYXBtaW5kZXItRml2ZVllYXJEYXRhLmNzdiIpDQoNCiMgZGVmaW5lIGEgY29weSBvZiB0aGUgb3JpZ2luYWwgZGF0YXNldCB0aGF0IHdlIHdpbGwgY2xlYW4gYW5kIHBsYXkgd2l0aCANCmdhcG1pbmRlciA8LSBnYXBtaW5kZXJfb3JpZw0KYGBgDQoNCg0KYGBge3J9DQpoZWFkKGdhcG1pbmRlcikNCmBgYA0KDQpgYGB7cn0NCm5hbWVzKGdhcG1pbmRlcikNCmBgYA0KDQpgYGB7cn0NCmRpbShnYXBtaW5kZXIpDQpgYGANCg0KIyMgbWFwICYgbW9kaWZ5IGZyb20gcHVycg0KDQojIyMgY2xhc3MNCg0KYGBge3J9DQpjbGFzcyhnYXBtaW5kZXIkY291bnRyeSkNCmBgYA0KDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBtYXBfY2hyKGNsYXNzKQ0KYGBgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBtb2RpZnkoY2xhc3MpDQpgYGANCg0KYG1vZGlmeSgpYCByZXR1cm5zIGluIHRoZSBzYW1lIG91dHB1dCBmb3JtYXQgYXMgaW5wdXQsIHNvIGl0IGlzIG5vdCBhIHN1aXRhYmxlIGNob2ljZSBpbiB0aGlzIGNhc2UNCg0KDQojIyMgbl9kaXN0aW5jdA0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUgbWFwX2RibChuX2Rpc3RpbmN0KQ0KYGBgDQoNCiMjIyBjbGFzcyArIG5fZGlzdGluY3QNCg0KYGBge3J9DQpnYXBtaW5kZXIgJT4lIG1hcF9kZih+ZGF0YS5mcmFtZShjbGFzcyA9IGNsYXNzKC54KSwNCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRpc3RpbmN0ID0gbl9kaXN0aW5jdCgueCkpKQ0KYGBgDQoNCm1ha2Ugc3VyZSB0byBwYXNzIC54IG90aGVyd2lzZSBpdCB3aWxsIG5vdCBwZXJmb3JtIGFjdGlvbiBvbiBjb2x1bW5zDQoNCmBOb3RlYCA6IHdlIGhhdmUgbWlzc2VkIHRoZSBjb2x1bW4gbmFtZXMgYWJvdmUNCg0KYEFkZGluZyBjb2x1bW4gbmFtZXNgDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBtYXBfZGYofmRhdGEuZnJhbWUoY2xhc3MgPSBjbGFzcygueCksDQogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkaXN0aW5jdCA9IG5fZGlzdGluY3QoLngpKSwNCiAgICAgICAgICAgICAgICAgICAgIC5pZCA9ICJjb2x1bW5fbmFtZSIpDQpgYGANCg0KZGVmaW5pbmcgLngNCg0KYGBge3J9DQpkYXRhLmZyYW1lKG5fZGlzdGluY3QgPSBuX2Rpc3RpbmN0KGdhcG1pbmRlciAlPiUgcGx1Y2soMSkpLA0KICAgICAgICAgICBjbGFzcyA9IGNsYXNzKGdhcG1pbmRlciAlPiUgcGx1Y2soMSkpKQ0KYGBgDQoNCmBgYHtyfQ0KY29udGluZW50X3llYXIgPC0gZ2FwbWluZGVyICU+JSBkaXN0aW5jdChjb250aW5lbnQsIHllYXIpDQpjb250aW5lbnRfeWVhcg0KYGBgDQoNCg0KYGBge3J9DQpjb250aW5lbnRzIDwtIGNvbnRpbmVudF95ZWFyICU+JSANCiAgICAgICAgICAgICAgICBwdWxsKGNvbnRpbmVudCkgJT4lIA0KICAgICAgICAgICAgICAgIGFzLmNoYXJhY3Rlcg0KDQp5ZWFycyA8LSBjb250aW5lbnRfeWVhciAlPiUgDQogICAgICAgICAgICBwdWxsKHllYXIpDQpgYGANCg0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUgDQogIGZpbHRlcihjb250aW5lbnQgPT0gY29udGluZW50c1sxXSwNCiAgICAgICAgIHllYXIgPT0geWVhcnNbMV0pICU+JSANCiAgDQogIGdncGxvdCgpICsNCiAgZ2VvbV9wb2ludChhZXMoeCA9IGdkcFBlcmNhcCwgeSA9IGxpZmVFeHAsIGNvbCA9IGNvdW50cnkpKSArDQogIGdndGl0bGUocGFzdGUoY29udGluZW50c1sxXSwgeWVhcnNbMV0pKQ0KYGBgDQoNCmBgYHtyfQ0KLnggPC0gY29udGluZW50c1sxXQ0KLnkgPC0geWVhcnNbMV0NCiAgDQpnYXBtaW5kZXIgJT4lIA0KICBmaWx0ZXIoY29udGluZW50ID09IC54LA0KICAgICAgICAgeWVhciA9PSAueSkgJT4lIA0KICANCiAgZ2dwbG90KCkgKw0KICBnZW9tX3BvaW50KGFlcyh4ID0gZ2RwUGVyY2FwLCB5ID0gbGlmZUV4cCwgY29sID0gY291bnRyeSkpICsNCiAgZ2d0aXRsZShwYXN0ZSgueCwgLnkpKQ0KYGBgDQoNCkFwcGx5aW5nIGFib3ZlIHRlc3QgY29kZSBmb3IgZ2VuZXJpYyB1c2FnZSBvYmplY3QNCg0KYGBge3J9DQpwbG90X2xpc3QgPC0gbWFwMigueCA9IGNvbnRpbmVudHMsIA0KICAgICAgICAgICAgICAgICAgLnkgPSB5ZWFycywNCiAgICAgLmYgPSB+e2dhcG1pbmRlciAlPiUgDQogIGZpbHRlcihjb250aW5lbnQgPT0gLngsDQogICAgICAgICB5ZWFyID09IC55KSAlPiUgDQogIA0KICBnZ3Bsb3QoKSArDQogIGdlb21fcG9pbnQoYWVzKHggPSBnZHBQZXJjYXAsIHkgPSBsaWZlRXhwLCBjb2wgPSBjb3VudHJ5KSkgKw0KICBnZ3RpdGxlKHBhc3RlKC54LCAueSkpDQogICAgICAgfSkNCmBgYA0KDQoNCmBgYHtyfQ0KcGxvdF9saXN0WzFdDQpgYGANCg0KYGBge3J9DQpwbG90X2xpc3RbWzIyXV0NCmBgYA0KDQpCZWxvdyBJIG5lc3QgdGhlIGdhcG1pbmRlciBkYXRhIGJ5IGNvbnRpbmVudC4NCg0KYGBge3J9DQpnYXBtaW5kZXJfbmVzdGVkIDwtIGdhcG1pbmRlciAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgZ3JvdXBfYnkoY29udGluZW50KSAlPiUgDQogICAgICAgICAgICAgICAgICAgICAgbmVzdCgpDQoNCmdhcG1pbmRlcl9uZXN0ZWQNCmBgYA0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXN0ZWQgJT4lIHVubmVzdCgpDQpgYGANCg0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXN0ZWQkZGF0YVtbMV1dDQpgYGANCg0KYGBge3J9DQpnYXBtaW5kZXJfbmVzdGVkJGRhdGFbWzJdXQ0KYGBgDQoNCg0KVG8gcHVsbCBvciBleHRyYWN0IGZyb20gaXQgYnkgaW5kZXgNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmVzdGVkICU+JSANCiAgcGx1Y2soMSkNCmBgYA0KDQpUbyBwdWxsIG9yIGV4dHJhY3QgZGF0YSBmcm9tIGl0IGJ5IGluZGV4DQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25lc3RlZCAlPiUgDQogIHBsdWNrKCJkYXRhIiwxKQ0KYGBgDQoNCg0KYGBge3J9DQp0aWJibGUobGlzdF9jb2wgPSBsaXN0KGMoMSwgNSwgNyksDQogICAgICAgICAgICAgICAgICAgICAgIDUsDQogICAgICAgICAgICAgICAgICAgICAgIGMoMTAsIDEwLCAxMSkpKSAlPiUgDQogICAgbXV0YXRlKGxpc3Rfc3VtID0gbWFwKC54ID0gbGlzdF9jb2wsIC5mID0gc3VtKSkNCmBgYA0KDQpzaW5jZSBtYXAgcmV0dXJucyBhIGxpdHMgaXRzZWxmLCBzbyB3ZSB3aWxsIG5lZWQgdG8gcHVsbCBmcm9tIHRoZSBsaXN0DQoNCmBgYHtyfQ0KdGliYmxlKGxpc3RfY29sID0gbGlzdChjKDEsIDUsIDcpLA0KICAgICAgICAgICAgICAgICAgICAgICA1LA0KICAgICAgICAgICAgICAgICAgICAgICBjKDEwLCAxMCwgMTEpKSkgJT4lIA0KICAgIG11dGF0ZShsaXN0X3N1bSA9IG1hcCgueCA9IGxpc3RfY29sLCAuZiA9IHN1bSkpICU+JSANCiAgcHVsbChsaXN0X3N1bSkNCmBgYA0KDQppdCBjb3VsZCBiZSBiZXR0ZXIgdG8gcmVzdWx0IG91dCBhIHZlIHRvciBpbnN0ZWFkIG9mIGxpc3QNCg0KYGBge3J9DQp0aWJibGUobGlzdF9jb2wgPSBsaXN0KGMoMSwgNSwgNyksDQogICAgICAgICAgICAgICAgICAgICAgIDUsDQogICAgICAgICAgICAgICAgICAgICAgIGMoMTAsIDEwLCAxMSkpKSAlPiUgDQogIG11dGF0ZShsaXN0X3N1bSA9IG1hcF9kYmwoLnggPSBsaXN0X2NvbCwgLmYgPSBzdW0pKQ0KYGBgDQoNCkhvdyB0byBnZXQgbWVhbiBmcm9tIGNvbHVtbiBsaXN0ZWQgdGliYmxlIGRhdGENCg0KYGBge3J9DQoueCA8LSBnYXBtaW5kZXJfbmVzdGVkICU+JSANCiAgICAgICAgcGx1Y2soImRhdGEiLCAxKQ0KYGBgDQoNCg0KYGBge3J9DQptZWFuKC54JGxpZmVFeHApDQpgYGANCk5vdyBhcHBseWluZyBtZWFuIGZ1bmN0aW9uIG9uIGFsbCBjb2x1bW4gbGlzdGVkIHRpYmxlIGRhdGENCg0KYGBge3J9DQpnYXBtaW5kZXJfbmVzdGVkICU+JSANCiAgbXV0YXRlKGF2Z19saWZlRXhwID0gbWFwX2RibChkYXRhLCB+e21lYW4oLngkbGlmZUV4cCl9KSkNCmBgYA0KDQojIyMgZml0dGluZyBhIGxpbmVhciBtb2RlbCBmb3IgZWFjaCBjb250aW5lcyAvIHJvdw0KDQpgYGB7cn0NCmdhcG1pbmRlcl9uZXN0ZWQgPC0gZ2FwbWluZGVyX25lc3RlZCAlPiUgDQogIG11dGF0ZShsbV9vYmogPSBtYXAoZGF0YSwgfmxtKGxpZmVFeHAgfiBwb3AgKyBnZHBQZXJjYXAgKyB5ZWFyLCBkYXRhID0gLngpKSkNCg0KZ2FwbWluZGVyX25lc3RlZA0KYGBgDQoNCiMjIyMgY2hlY2tpbmcgbGluZWFyIG1vZGVsIGZvciBmaXJzdCBjb250aW5lbnQNCg0KYGBge3J9DQpnYXBtaW5kZXJfbmVzdGVkICU+JSBwbHVjaygibG1fb2JqIiwgMSkNCmBgYA0KDQojIyMgQWRkaW5nIFByZWRpY3Rpb25zDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25lc3RlZCA8LSBnYXBtaW5kZXJfbmVzdGVkICU+JSANCiAgbXV0YXRlKHByZWQgPSBtYXAyKC54ID0gbG1fb2JqLCAueSA9IGRhdGEsIGZ1bmN0aW9uKC54LC55KSBwcmVkaWN0KC54LCAueSkpKQ0KDQpnYXBtaW5kZXJfbmVzdGVkDQpgYGANCg0KY2FuIGFsc28gYmUgd3JpdHRlbiBhcyANCg0KYGBge3J9DQpnYXBtaW5kZXJfbmVzdGVkICU+JSANCiAgbXV0YXRlKHByZWQgPSBtYXAyKGxtX29iaiwgZGF0YSwgZnVuY3Rpb24oLmxtLCAuZGF0YSkgcHJlZGljdCgubG0sIC5kYXRhKSkpDQoNCmBgYA0KDQoNCiMjIyMgQ2FsYy4gY29ycmVsYXRpb24gcHJlZCByZXBzLiB2cyB0cnVlIHJlc3AuDQoNCmBgYHtyfQ0KZ2FwbWluZGVyX25lc3RlZCA8LSBnYXBtaW5kZXJfbmVzdGVkICU+JSANCiAgbXV0YXRlKGNvciA9IG1hcDJfZGJsKHByZWQsIGRhdGEsIGZ1bmN0aW9uKC5wcmVkLCAuZGF0YSkgY29yKC5wcmVkLCAuZGF0YSRsaWZlRXhwKSkpDQoNCmdhcG1pbmRlcl9uZXN0ZWQNCmBgYA0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUgDQogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIA0KICBuZXN0ICU+JSANCiAgbXV0YXRlKGxtX29iaiA9IG1hcChkYXRhLCB+bG0obGlmZUV4cCB+IHBvcCArIHllYXIgKyBnZHBQZXJjYXAsIGRhdGEgPSAuKSkpICU+JSANCiAgbXV0YXRlKGxtX3RpZHkgPSBtYXAobG1fb2JqLCBicm9vbTo6dGlkeSkpDQpgYGANCg0KDQpgYGB7cn0NCmdhcG1pbmRlciAlPiUgDQogIGdyb3VwX2J5KGNvbnRpbmVudCkgJT4lIA0KICBuZXN0ICU+JSANCiAgbXV0YXRlKGxtX29iaiA9IG1hcChkYXRhLCB+bG0obGlmZUV4cCB+IHBvcCArIHllYXIgKyBnZHBQZXJjYXAsIGRhdGEgPSAuKSkpICU+JSANCiAgbXV0YXRlKGxtX3RpZHkgPSBtYXAobG1fb2JqLCBicm9vbTo6dGlkeSkpICU+JSANCiAgdW5ncm91cCgpICU+JSANCiAgdHJhbnNtdXRlKGNvbnRpbmVudCwgbG1fdGlkeSkgJT4lIA0KICB1bm5lc3QoY29scyA9IGMobG1fdGlkeSkpDQpgYGANCg0KDQojIyMgc3BsaXQgZnVuY3Rpb24NCg0KdGhpcyB3aWxsIHNwbGl0IHRoZSBkYXRhIGZyYW1lIG9uIGJhc2lzaSBvZiBmYWN0b3JzIHByb3ZpZGVkIGZyb20gdGhlIHZhcmlhYmxlDQoNCmBgYHtyfQ0KZ2FwbWluZGVyICU+JSBzcGxpdChnYXBtaW5kZXIkY29udGluZW50KQ0KYGBgDQoNCg0KYGBge3J9DQpzZXQuc2VlZCgyMzQ4OSkNCg0KZ2FwbWluZGVyX2xpc3QgPC0gZ2FwbWluZGVyICU+JSANCiAgc3BsaXQoZ2FwbWluZGVyJGNvbnRpbmVudCkgJT4lIA0KICBtYXAofnNhbXBsZV9uKC4sIDUpKQ0KDQpnYXBtaW5kZXJfbGlzdA0KYGBgDQoNCg0KIyMjIGtlZXAoKQ0KDQpmdW5jdGlvbiB0byBsaW1pdC8gZmlsdGVyIGRhdGEgZnJhbWUgd2l0aCBjb25kaXRpb25zDQoNCmRpc2NhcigpIGlzIG9wcG9zaXRlIG9mIGtlZXANCg0KYGBge3J9DQpnYXBtaW5kZXJfbGlzdCAlPiUgDQogIGtlZXAofnttZWFuKC54JGxpZmVFeHApID4gNzB9KQ0KICANCmBgYA0KDQoNCiMjIyBSZWR1Y2UoKQ0KDQpgcmVkdWNlKClgIGlzIGRlc2lnbmVkIHRvIGNvbWJpbmUgKHJlZHVjZXMpIGFsbCBvZiB0aGUgZWxlbWVudHMgb2YgYSBsaXN0IGludG8gYSBzaW5nbGUgb2JqZWN0IGJ5IGl0ZXJhdGl2ZWx5IGFwcGx5aW5nIGEgYmluYXJ5IGZ1bmN0aW9uIChhIGZ1bmN0aW9uIHRoYXQgdGFrZXMgdHdvIGlucHV0cykuDQoNCmBgYHtyfQ0KcmVkdWNlKGMoMSwgMiwgMyksIHN1bSkNCmBgYA0KDQojIyMgYWNjdW11bGF0ZSgpDQoNCmFsc28gcmV0dXJucyB0aGUgaW50ZXJtZWRpYXRlIHZhbHVlcy4NCg0KYGBge3J9DQphY2N1bXVsYXRlKGMoMSwgMiwgMyksIHN1bSkNCmBgYA0KDQpgUmVkdWNlYCBjYW4gYmUgdXNlZnVsIGluIGNvbWJpbmluZyBjb2x1bW5zIGJ5IHVzaW5nIGxlZnRfam9pbiBldGMuIG9yIHRvIGRvIHJlcGVhdGVkIGByYmluZCgpYA0KDQpgYGB7cn0NCmdhcG1pbmRlcl9saXN0ICU+JSANCiAgcmVkdWNlKHJiaW5kKQ0KYGBgDQoNCg0KDQojIyMgTG9naWNhbCBzdGF0ZW1lbnRzIGZvciBsaXN0cw0KDQpgZXZlcnkoKWAsIGBzb21lKClgDQoNCkZvciBpbnN0YW5jZSB0byBhc2sgd2hldGhlciBldmVyeSBjb250aW5lbnQgaGFzIGF2ZXJhZ2UgbGlmZSBleHBlY3RhbmN5IGdyZWF0ZXIgdGhhbiA3MCwgeW91IGNhbiB1c2UgZXZlcnkoKQ0KDQpgYGB7cn0NCmdhcG1pbmRlcl9saXN0ICU+JSBldmVyeSh+e21lYW4oLngkbGlmZSkgPiA3MCB9KQ0KYGBgDQoNCg0KYGBge3J9DQpnYXBtaW5kZXJfbGlzdCAlPiUgc29tZSh+e21lYW4oLngkbGlmZSkgPiA3MH0pDQpgYGANCg0KDQojIyMgaGFzX2VsZW1lbnQoKQ0KDQp0aGlzIGlzIGVxdWl2YWxlbnQgb2YgJWluJQ0KDQpgYGB7cn0NCmxpc3QoMSwgYygyLCA1LCAxKSwgImEiKSAlPiUgaGFzX2VsZW1lbnQoImEiKQ0KYGBgDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0KDQoNCg0K